
#if 0
__FBSDID("$FreeBSD: src/usr.bin/bsdiff/bspatch/bspatch.c,v 1.1 2005/08/06 01:59:06 cperciva Exp $");
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <err.h>
#include <unistd.h>
#include <fcntl.h>
#include <lzma.h>

#define MIN(x,y) (((x)<(y)) ? (x) : (y))

#define ifdebug 0

static off_t offtin(u_char *buf)
{
	off_t y;

	y=buf[7]&0x7F;
	y=y*256;y+=buf[6];
	y=y*256;y+=buf[5];
	y=y*256;y+=buf[4];
	y=y*256;y+=buf[3];
	y=y*256;y+=buf[2];
	y=y*256;y+=buf[1];
	y=y*256;y+=buf[0];

	if(buf[7]&0x80) y=-y;

	return y;
}


/* xzfile is a provisional stdio-like interface to xz/lzma2-compressed data.
 * liblzma does not currently include this functionality. The interface is
 * read-only and only supports sequential access. */

typedef struct {
	/* in and out are the underlying buffers to be used with lzma_stream. */
	u_char in[10];
	u_char out[15];

	lzma_stream ls;
	FILE *f;

	/* read_out points to the first byte in out not yet consumed by an
	 * xzread call. read_out_len tracks the amount of data available in
	 * out beginning at read_out. */
	u_char *read_out;
	size_t read_out_len;

	/* Error and end-of-file indicators. */
	lzma_ret err;
	int eof;
} xzfile;

/* Initializes and returns a new xzfile pointer that will read from f. On
 * failure, returns NULL. If err is non-NULL, it will be set to indicate any
 * error that may have occurred. */
static xzfile *xzdopen(FILE *f, lzma_ret *err)
{
	xzfile *xzf;
	lzma_stream ls = LZMA_STREAM_INIT;
	uint64_t memlimit;

	if (!(xzf = malloc(sizeof(xzfile)))) {
		if (err) {
			*err = LZMA_MEM_ERROR;
		}
		return NULL;
	}

	xzf->ls = ls;
	xzf->f = f;

	xzf->read_out = xzf->out;
	xzf->read_out_len = 0;

	xzf->err = LZMA_OK;
	xzf->eof = 0;

	/* Use the same memory limits used by xzdec and xz. Use 40% of
	 * physical memory if 80MB or more, otherwise use 80% of physical
	 * memory if 80MB or less, otherwise use 80MB. If physical memory
	 * can't be determined, use 128MB. These limits should be sufficient
	 * for any decompression on any general-purpose system. */
	memlimit = 80 * 1024 * 1024;

	xzf->err = lzma_stream_decoder(&xzf->ls, memlimit,
				       LZMA_TELL_NO_CHECK |
					   LZMA_TELL_UNSUPPORTED_CHECK);
	if (xzf->err != LZMA_OK) {
		if (err) {
			*err = xzf->err;
		}
		free(xzf);
		return NULL;
	}

	if (err) {
		*err = xzf->err;
	}
	return xzf;
}

/* Closes an xzfile opened by xzopen, freeing all memory and closing all
 * files. Returns LZMA_OK normally, or LZMA_STREAM_END if fclose fails. */
static lzma_ret xzclose(xzfile *xzf)
{
	lzma_ret lzma_err = LZMA_OK;

	lzma_end(&xzf->ls);
	if (fclose(xzf->f) != 0) {
		lzma_err = LZMA_STREAM_END;
	}
	free(xzf);

	return lzma_err;
}

/* Reads len uncompressed bytes from xzf into buf. Returns the number of bytes
 * read, which may be less than len at the end of the file. Upon error, if
 * err is non-NULL, it will be set to an appropriate value, which will either
 * be a return value from lzma_code (with the exception of LZMA_STREAM_END,
 * which is remapped to LZMA_OK), or LZMA_STREAM_END to indicate an I/O error.
 */
static size_t xzread(xzfile *xzf, u_char *buf, size_t len, lzma_ret *err)
{
	lzma_action action = LZMA_RUN;
	size_t copylen;
	size_t nread = 0;

	*err = LZMA_OK;

	while (xzf->err == LZMA_OK && len > 0) {
		if (xzf->read_out_len == 0) {
			/* No unconsumed data is available, need to run
			 * lzma_code to decompress. */

			if (xzf->ls.avail_in == 0 && xzf->eof) {
				return 0;
			}
			if (xzf->ls.avail_in == 0 && !xzf->eof) {
				/* No input data available, need to read. */
				xzf->ls.next_in = xzf->in;
				xzf->ls.avail_in = fread(xzf->in, 1, sizeof(xzf->in),
							 xzf->f);
				if (ferror(xzf->f)) {
					/* Map I/O errors to LZMA_STREAM_END. */
					xzf->err = LZMA_STREAM_END;
					*err = xzf->err;
					return 0;
				} else if (feof(xzf->f)) {
					xzf->eof = 1;
				}
			}

			/* Use the full output buffer. */
			xzf->ls.next_out = xzf->out;
			xzf->ls.avail_out = sizeof(xzf->out);

			/* There must be something to decode. */
			if (xzf->ls.avail_in == 0) {
				xzf->err = LZMA_BUF_ERROR;
				*err = xzf->err;
				return 0;
			}

			/* LZMA_FINISH is not critical because
			 * LZMA_CONCATENATED is not in use. */
			if (xzf->eof) {
				action = LZMA_FINISH;
			}

			/* Run the decoder. */
			xzf->err = lzma_code(&xzf->ls, action);
			if (xzf->err == LZMA_STREAM_END) {
				xzf->eof = 1;
				xzf->err = LZMA_OK;
				/* if the stream ended, but no bytes were outputed.. we're at the end */
				if (xzf->ls.avail_out == sizeof(xzf->out)) {
					len = 0;
				}
			} else if (xzf->err != LZMA_OK) {
				*err = xzf->err;
				return 0;
			}

			/* Everything that was decoded is now available for
			 * reading into buf. */
			xzf->read_out = xzf->out;
			xzf->read_out_len = sizeof(xzf->out) - xzf->ls.avail_out;
		}

		/* Copy everything available up to len, and push some
		 * pointers. */
		copylen = xzf->read_out_len;
		if (copylen > len) {
			copylen = len;
		}
		memcpy(buf, xzf->read_out, copylen);
		nread += copylen;
		buf += copylen;
		len -= copylen;
		xzf->read_out += copylen;
		xzf->read_out_len -= copylen;
	}

	*err = xzf->err;
	return nread;
}


int main(int argc,char * argv[])
{
	FILE * f, * f_data;
	int fileread,filewrite;
	ssize_t newsize;
	u_char header[40],buf[8];
	off_t oldpos,newpos;
	off_t ctrl[3];
	off_t lenread;
	off_t i;

	off_t copynum;
	off_t addnum;
	off_t datapos;
	u_char *writeBuf;//读取缓存区
	u_char *readBuf;//写入缓存区
	off_t bufSize;//缓存区大小
	int blockSize;
	int blockCount;

	lzma_stream strm = LZMA_STREAM_INIT;
	lzma_ret ret;
	xzfile *xz = malloc(sizeof(xzfile));
	xzfile *xz_data = malloc(sizeof(xzfile));

	off_t oldsize;

	if(argc!=3) errx(1,"usage: %s oldfile patchfile\n",argv[0]);

	/* Open patch file */
	if ((f = fopen(argv[2], "r")) == NULL)
		err(1, "fopen(%s)", argv[2]);

	/* Open patch file */
	if ((f_data = fopen(argv[2], "r")) == NULL)
		err(1, "fopen(%s)", argv[2]);
	
	
	/* Header is
	0	8	 "BSDIFF40"
	8   8    size of newfile
	16	8	 size of block
	24	8	 count of block
	32	8	 pos of data */
	/* File is
	0	40	Header
	40	??	ctrl(x,y,z)
	??	??	data

	*/

	if (fread(header, 1, 40, f) < 40) {
		if (feof(f))
			errx(1, "fail to read header\n");
		err(1, "fread(%s)", argv[2]);
	}

	/* Check for appropriate magic */
	if (memcmp(header, "BSDIFF40", 8) != 0)
		errx(1, "wrong header\n");

	/* Read from header */
	newsize=offtin(header+8);
	blockSize=offtin(header+16);
	blockCount=offtin(header+24);
	datapos=offtin(header+32);
	if(ifdebug){
		printf("newsize:%ld\n",newsize);
		printf("blockSize:%d\n",blockSize);
		printf("blockCount:%d\n",blockCount);
		printf("datapos:%ld\n",datapos);
	}

	writeBuf = malloc((blockSize+1)*sizeof(u_char));
	readBuf = malloc((blockSize+1)*sizeof(u_char));

	fseek(f_data,datapos,SEEK_SET);

	xz = xzdopen(f,&ret);
	xz_data = xzdopen(f_data,&ret);


	if ((fileread = open(argv[1],O_RDONLY)) < 0)
		err(1, "fopen(%s)", argv[1]);

	oldsize = lseek(fileread,0,SEEK_END);
	lseek(fileread,0,SEEK_SET);
	if(ifdebug) printf("oldsize:%ld\n",oldsize);


	if ((filewrite = open(argv[1],O_RDWR)) < 0)
		err(1, "fopen(%s)", argv[1]);
	lseek(fileread,0,SEEK_SET);
    lseek(filewrite,0,SEEK_SET);

	off_t copyedCount=0;
	off_t lastend_t = 0;
	off_t lastend_f = 0;

	off_t readPos=0;
	
	for(int bi=0;bi<blockCount;bi++){
		
		lenread = xzread(xz, buf, 8, &ret);
		int blockId = offtin(buf);
		if(ifdebug) printf("------block%d:id=%d-----\n",bi,blockId);
		off_t writeSum=0;
		int currBlockSize = blockSize;
		off_t startPos,endPos,currPos;
		startPos = blockId*blockSize;
		currPos = startPos;
		endPos = MIN(startPos+blockSize-1,newsize-1);
		if(blockId==(blockCount-1)){
			currBlockSize = newsize-blockSize*blockId;		
		}

		while(writeSum<currBlockSize){
			for(i=0;i<=2;i++) {
				lenread = xzread(xz, buf, 8, &ret);
				if ((lenread < 8) ){
						errx(1, "bad copy\n");
					}
				ctrl[i]=offtin(buf);
			};
			if(ifdebug) printf("[%ld,%ld,%ld]\n",ctrl[0],ctrl[1],ctrl[2]);

			off_t copyLen = ctrl[0];
			off_t addLen = ctrl[1];
			readPos += ctrl[2];
			lseek(fileread,ctrl[2],SEEK_CUR);
			if(ifdebug) printf("Copy[%ld,%ld,%ld]\n",readPos,blockId*blockSize+writeSum,copyLen);
			lenread = xzread(xz_data, readBuf, copyLen, &ret);
			if (lenread < copyLen)
				errx(1, "bad copy\n");

			read(fileread,writeBuf+writeSum,copyLen);
			
			/* Add old data to diff string */
			for(int i=0;i<copyLen;i++){
				 writeBuf[i+writeSum]=writeBuf[i+writeSum]+readBuf[i];
			}
			writeSum += copyLen;
			readPos += copyLen;
			if(ifdebug) printf("Add[%ld,%ld]\n",bi*blockSize+writeSum,addLen);
			lenread = xzread(xz_data, writeBuf + writeSum, addLen, &ret);
			if (lenread < addLen)
				errx(1, "bad add\n");
			writeSum += addLen;
		}
		lseek(filewrite,startPos,SEEK_SET);
		if(writeSum == currBlockSize) {
			write(filewrite,writeBuf,currBlockSize);
		}else{
			errx(1, "wrong blocksize\n");
		}

	}

	
	xzclose(xz);
	xzclose(xz_data);
	ftruncate(filewrite,newsize);

	//if oldfile bigger than newfile , delete extra data
	ftruncate(filewrite,newsize);
	if (close(fileread) || close(filewrite))
		err(1, "fclose(%s)", argv[1]);

	free(writeBuf);
	free(readBuf);
	return 0;
}
